//
//	License: The MIT License (MIT)
//  Copyright (c) 2010 Kai Bidstrup 
//
//	http://ua6281.codeplex.com/
//

using System;
using System.Text;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Collections;

namespace A6281 {
	public class Multiple : IDisposable {

		protected SPI.Configuration configuration;
		protected SPI serialPeripheralInterface;
		protected OutputPort latchOutput;
		protected OutputPort outputEnableOutput;
		protected int latchDelay;

		public ChipRegisters[] Registers {
			get;
			protected set;
		}

		public Multiple( int daisyChangedModuleCount, Cpu.Pin chipSelectPin, Cpu.Pin latchPin, Cpu.Pin outputEnablePin, SPI.SPI_module spiModule ) {
			try {
				this.Registers = new ChipRegisters[ daisyChangedModuleCount ];
				for ( int index = 0; index < this.Registers.Length; index++ )
					this.Registers[ index ] = new ChipRegisters();

				this.configuration = new SPI.Configuration( chipSelectPin
					, true
					, 0
					, 0
					, false
					, true
					, 800
					, spiModule );


				this.latchDelay = daisyChangedModuleCount * 4;

				this.latchOutput = new OutputPort( latchPin, false );
				this.latchOutput.Write( false );

				this.outputEnableOutput = new OutputPort( outputEnablePin, true );
				this.outputEnableOutput.Write( false );

				this.serialPeripheralInterface = new SPI( this.configuration );
			}
			catch ( Exception ex ) {
				Debug.Print( ex.Message );
				throw ex;
			}
		}

		public bool On {
			get {
				return !this.outputEnableOutput.Read();
			}
			set {
				this.outputEnableOutput.Write( !value );
			}
		}

		public void SetCorrectionImmediate() {
			byte[] packet = new byte[ this.Registers.Length * ChipRegisters.PacketSize ];

			for ( int chipIndex = 0; chipIndex < this.Registers.Length; chipIndex++ ) {
				this.Registers[ chipIndex ].InsertCorrectionPacketData( packet, chipIndex * ChipRegisters.PacketSize );
			}

			this.serialPeripheralInterface.Write( packet );
			Thread.Sleep( latchDelay );
			this.latchOutput.Write( true );
			Thread.Sleep( latchDelay );
			this.latchOutput.Write( false );
		}

		public void SetColorImmediate(  ) {
			try {

				byte[] packet = new byte[ this.Registers.Length * ChipRegisters.PacketSize ];

				for ( int chipIndex = 0; chipIndex < this.Registers.Length; chipIndex++ ) {
					this.Registers[ chipIndex ].InsertColorPacketData( packet, chipIndex * ChipRegisters.PacketSize );
				}

				this.serialPeripheralInterface.Write( packet );

				Thread.Sleep( this.latchDelay );
				this.latchOutput.Write( true );
				Thread.Sleep( this.latchDelay );
				this.latchOutput.Write( false );
			}
			catch ( Exception ex ) {
				Debug.Print( ex.Message );
				throw ex;
			}
		}

		public void SetColorImmediate( LightSet lightSet ) {
			for ( int index = 0; index < lightSet.Colors.Length && index < this.Registers.Length; index++ ) {
				this.Registers[ index ].Color = lightSet.Colors[ index ];
			}
			this.SetColorImmediate();
		}

		public void SetColorImmediate( ChipColor singleColor ) {
			for ( int index = 0; index < this.Registers.Length; index++ ) {
				this.Registers[ index ].Color = singleColor;
			}
			this.SetColorImmediate();
		}

		public void SetColorImmediate( LightSequence lightSequence ) {
			for ( int index = 0; index < lightSequence.LightSets.Length; index++ ) {
				this.SetColorImmediate( lightSequence.LightSets[ index ] );
				this.SetCorrectionImmediate();
				Thread.Sleep( lightSequence.LightSets[ index ].DisplayTimeInMilliSeconds );
			}
		}

		public void SetColorImmediate( ChipColorSet chipColorSet ) {
			IEnumerator chipColorEnumerator = chipColorSet.GetEnumerator();
			for( int index = 0; index < this.Registers.Length; index++ ) {
				ChipColor chipColor = ( ChipColor )chipColorEnumerator.Current;
				this.Registers[ index ].Color = chipColor;
				chipColorEnumerator.MoveNext();
			}
			this.SetColorImmediate();
		}

		public void RunChipColorSequence( ChipColorSequence colorSequence, DelaySet delaySet ) {
			// run in infinite loop
			IEnumerator colorSetEnumerator = colorSequence.GetEnumerator();
			IEnumerator delaySetEnumerator = delaySet.GetEnumerator();

			while( true ) {
				Delay delay = ( Delay )delaySetEnumerator.Current;
				ChipColorSet chipColorSet = ( ChipColorSet )colorSetEnumerator.Current;

				this.SetColorImmediate( chipColorSet );

				colorSetEnumerator.MoveNext();
				delaySetEnumerator.MoveNext();
				delay.Execute();
			}
		}

		public void RunChipColorSequence( ChipColorSequence colorSequence, DelaySet delaySet, TimeSpan timeSpanRunUntil ) {
			DateTime until = DateTime.Now + timeSpanRunUntil;

			IEnumerator colorSetEnumerator = colorSequence.GetEnumerator();
			IEnumerator delaySetEnumerator = delaySet.GetEnumerator();

			while( until > DateTime.Now ) {
				Delay delay = ( Delay )delaySetEnumerator.Current;
				ChipColorSet chipColorSet = ( ChipColorSet )colorSetEnumerator.Current;

				this.SetColorImmediate( chipColorSet );

				colorSetEnumerator.MoveNext();
				delaySetEnumerator.MoveNext();
				delay.Execute();
			}
		}
		#region IDisposable Members

		public void Dispose() {
			this.outputEnableOutput.Dispose();
			this.outputEnableOutput = null;
			this.latchOutput.Dispose();
			this.latchOutput = null;
			this.serialPeripheralInterface.Dispose();
			this.serialPeripheralInterface = null;
			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
